Reflect.defineProperty(String.prototype, 'Ab_decodeAtoB_bA', {
	get(this: String){
		return decodeURIComponent(atob(this.valueOf()));
	},
	enumerable: true,
	configurable: false,
});
Reflect.defineProperty(String.prototype, 'Ab_encodeBtoA_bA', {
	get(this: String){
		return btoa(encodeURIComponent(this.valueOf()));
	},
	enumerable: true,
	configurable: false,
});

Number.prototype.Ab_inRange_bA = function(this: Number, a, b){
	let value = this.valueOf();

	if ([value, a, b].includes(NaN)) return false;

	let [va, vb] = [value - a, value - b].map(v => [-Infinity, Infinity].includes(v) ? v<0 ? -1 : 1 : v);

	return va * vb <= 0;
}

Reflect.defineProperty(Array.prototype, 'Ab_lastElement_bA', {
	get(this: Array<any>){
		return this.length>0 ? Reflect.get(this, this.length-1) : undefined;
	},
	set(this: Array<any>, value: any){
		Reflect.set(this, this.length>0 ? this.length-1 : 0, value);
	},
	enumerable: true,
	configurable: false,
});

Array.prototype.Ab_forWait_bA = async function(this: Array<any>, call){
	for(let i = 0; i < this.length; i++) await call(this[i], i, this);
}

const __Global__ = window ?? globalThis;
if(!Reflect.has(__Global__, 'abi')) Reflect.set(__Global__, 'abi', {__ability__: 'Ab_ability_bA'});
['Ab_string_bA', 'Ab_number_bA', 'Ab_array_bA', 'Ab_log_bA', 'Ab_color_bA', 'Ab_local_bA', 'Ab_time_bA']
.forEach(space_name=>{
	if(!Reflect.has(abi, space_name)) Reflect.set(abi, space_name, {[`${space_name}_space`]: space_name});
});

abi.Ab_vtype_bA = function(value){
	let type = typeof value;
	return type=='object' ? value==null ? 'null' : Array.isArray(value) ? 'array' : 'object' : type;
}

abi.Ab_same_bA = function(a, b, record){
	if(a===b) return true;

	let [ta, tb] = [a, b].map(_=>abi.Ab_vtype_bA(_));

	if(ta!=tb) return [ta, tb].every(t=>['number', 'bigint'].includes(t)) ? a==b : false;

	if(!['array', 'object'].includes(ta)) return a==b;

	if(!record) record = {cache: [], mark: new Map(), list: []};

	let token = [a, b].map(v=>{
		if(record.mark.has(v)) return record.mark.get(v);

		let mark = abi.Ab_string_bA.Ab_randomToken_bA(3, 36, _t=>!record.list.includes(_t));
		record.list.push(mark);
		record.mark.set(v, mark);
		return mark;
	}).sort().join('::');

	if(record.cache.includes(token)) return true;
	record.cache.push(token);

	let [key_a, key_b] = [a, b].map(v=>Reflect.ownKeys(v).filter(k=>v[k]!=undefined && v[k]!=null).sort());

	if(key_a.length==key_b.length && key_a.every((k, i)=>k==key_b[i])) return key_a.every(k=>abi.Ab_same_bA(a[k], b[k], record));

	return false;
}

abi.Ab_pickValueByType_bA = function<T extends [abi.Ab_type_string_bA, any][]>(values: any[], types: T){
	let map = values.reduce((m, v)=>{
		let t = abi.Ab_vtype_bA(v);
		if(m[t]==undefined) m[t] = [];

		Array.prototype.unshift.call(m[t], v);
		return m;
	}, {});

	return types.map(picker=>{
		let v = Reflect.get(map, picker[0]);
		return v ? v.length>1 ? Array.prototype.pop.call(v) : v[0] : picker[1];
	}) as abi.Ab_TypeArray_bA<abi.Ab_PickFirst_bA<T>>;
}

abi.Ab_delay_bA = function(delay, call, ...args){
	return new Promise((s, j)=>{
		let id = setTimeout((c, a)=>{
			clearTimeout(id);
			s(typeof c=='function' ? c(...a) : c);
		}, delay * 1000, call, args)
	});
}

abi.Ab_drawLots_bA = function(bucket){
	if(bucket.length<=0) return void 0;
	if(bucket.length==1) return bucket[0][0];

	let data_list = [];
	let weight_list = [];
	let total = 0;

	for(let [data, weight] of bucket){
		let v = Number(weight);
		if(!(v > 0)) continue;

		total += v;
		data_list.push(data);
		weight_list.push(total);
	}

	let point = Math.random() * total;
	let i = 0;
	while(point >= weight_list[i]) i++;

	return data_list[i];
}

abi.Ab_fusionData_bA = function(target, ...list){
	if(target instanceof Object){
		if(Array.isArray(target)){
			//@ts-ignore
			list.forEach(data=>Array.isArray(data) ? target.push(...data) : target.push(data));
		}else if(!(target instanceof Function)){
			let ls = list.filter(data=>data instanceof Object && !(data instanceof Function));
			let map = ls.reduce((m, data, i)=>{
				Reflect.ownKeys(data).forEach(key=>m.set(key, i));
				return m;
			}, new Map());
			//@ts-ignore
			map.forEach((i, key)=>Reflect.set(target, key, Reflect.get(ls[i], key)));
		}
	}else{
		if(typeof target == 'bigint'){
			list.forEach(data=>{
				switch(typeof data){
					//@ts-ignore
					case 'boolean': data = Number(data); case 'object': case 'undefined': data = BigInt(0); case 'number': if(isNaN(data)) data = 0; data = BigInt(data); break;
					//@ts-ignore
					case 'symbol': data = String(data); break;
				}
				// @ts-ignore
				target += data;
			})
		}else if(['string', 'symbol'].includes(typeof target)){
			//@ts-ignore
			target = String(target);
			//@ts-ignore
			list.forEach(data=>target += (typeof data=='symbol' ? String(data) : data));
		}else{
			list.forEach(data=>{
				switch(typeof data){
					//@ts-ignore
					case 'bigint': target = Number(target); if(isNaN(target)) target = BigInt(0); break;
					//@ts-ignore
					case 'symbol': data = String(data);
				}
				//@ts-ignore
				target += data;
			})
		}
	}

	return target as any;
}

abi.Ab_cloneData_bA = function(data, record){
	let t = abi.Ab_vtype_bA(data);

	if(['string', 'number', 'boolean', 'bigint', 'undefined', 'null'].includes(t)) return data;
	else if(t == 'function') return Function.prototype.bind.call(data, null);
	else if(t == 'symbol') return Symbol(data['description']);
	else{
		if(!record) record = new Map();

		if(record.has(data)) return record.get(data);

		let target;
		if(t=='array') target = [];
		else target = {};

		record.set(data, target);

		//@ts-ignore
		if(t=='array') data.forEach(el=>target.push(abi.Ab_cloneData_bA(el)));
		else{
			//@ts-ignore
			Reflect.ownKeys(data).forEach(key=>Reflect.set(target, key, Reflect.get(data, key)));
			//@ts-ignore
			Reflect.setPrototypeOf(target, Reflect.getPrototypeOf(data));
		}

		return target;
	}
}

abi.Ab_CountAverage_bA = class{
	private Ab_count_bA = 0;
	private Ab__average_bA: number;

	constructor(init?: number){
		if(init) this.Ab_add_bA(init);
	}

	public get Ab_average_bA(){
		return this.Ab__average_bA ?? 0;
	}

	public Ab_add_bA(value: number){
		this.Ab_count_bA++;
		this.Ab__average_bA = this.Ab_average_bA + (value - this.Ab_average_bA) / this.Ab_count_bA;
		return this.Ab__average_bA;
	}

	public Ab_clean_bA(){
		this.Ab_count_bA = 0;
		this.Ab__average_bA = 0;
	}
}

abi.Ab_NudityPromise_bA = class<V>{
	private Ab_inited_bA: boolean;
	private Ab_status_bA: 'waiting'|'resolve'|'reject';
	private Ab_result_bA: any;
	private Ab__resolve_call__bA: (value: V | PromiseLike<V>)=>void;
	private Ab__reject_call__bA: (reason: any)=>void;

	public Ab_promise_bA: Promise<V>;

	constructor(){
		this.Ab_inited_bA = false;
		this.Ab_status_bA = 'waiting';
		this.Ab_promise_bA = new Promise((s, j)=>{
			this.Ab__resolve_call__bA = s;
			this.Ab__reject_call__bA = j;
			this.Ab_inited_bA = true;
			this.Ab_reply_bA();
		});
	}

	public Ab_resolve_bA(value: V | PromiseLike<V>){
		this.Ab_re_call_bA('resolve', value);
	}

	public Ab_reject_bA(reason: any){
		this.Ab_re_call_bA('reject', reason);
	}

	private Ab_re_call_bA(status: 'resolve'|'reject', result: any){
		if(this.Ab_status_bA=='waiting'){
			[this.Ab_status_bA, this.Ab_result_bA] = [status, result];
			this.Ab_reply_bA();
		}
	}

	private Ab_reply_bA(){
		if(this.Ab_inited_bA && this.Ab_status_bA!='waiting'){
			switch(this.Ab_status_bA){
				case 'resolve': this.Ab__resolve_call__bA(this.Ab_result_bA); break;
				case 'reject': this.Ab__reject_call__bA(this.Ab_result_bA); break;
			}
		}
	}
}

abi.Ab_JsonString_bA = function(data){
	let tp = typeof data;
	if(tp != 'object') return JSON.stringify(data);
	if(data == null) return 'null';

	let copy_map: Map<any, any> = new Map();
	let token_map: Map<any, string> = new Map();
	let token_cache = [];

	let data_list = [data];
	copy_map.set(data, Array.isArray(data) ? [] : {});
	token_map.set(data, abi.Ab_string_bA.Ab_randomToken_bA(5, 36, tlkn=>!token_cache.includes(tlkn)));

	for(let i = 0; i < data_list.length; i++){
		let item = data_list[i];
		let copy = copy_map.get(item);
		let key_list: Array<string|number|symbol> = Array.isArray(item) ? abi.Ab_array_bA.Ab_create_bA(item.length, i=>i) : Reflect.ownKeys(item);
		let data_flag: Array<[string, string|number|symbol]> = [];

		for(let key of key_list){
			let value = Reflect.get(item, key);

			if(typeof value != 'object') Reflect.set(copy, key, value);
			else if(value == null) Reflect.set(copy, key, null);
			else if(token_map.has(value)) Reflect.set(copy, key, '<-[ref]->'+token_map.get(value));
			else{
				let token = abi.Ab_string_bA.Ab_randomToken_bA(5, 36, t=>!token_cache.includes(t));
				let cp = Array.isArray(value) ? [] : {};
				token_map.set(value, token);
				copy_map.set(value, cp);
				data_list.push(value);
				if(Array.isArray(item)){
					Reflect.set(copy, key, cp);
					data_flag.push([token, key]);
				}else Reflect.set(copy, token+'<-[data]->'+String(key), cp);
			}
		}

		if(data_flag.length>0 && Array.isArray(item)) copy.push(data_flag.map(el=>el[0] + '<-[data]->' + String(el[1])).join(';'));
	}

	return token_map.get(data) + '<-[data]->' + JSON.stringify(copy_map.get(data));
}

abi.Ab_parseJson_bA = function(json_string){
	let token: string;
	let data: any;
	if(json_string.includes('<-[data]->')) [token, json_string] = json_string.replace('<-[data]->', '->[data]<-').split('->[data]<-');

	try{
		data = JSON.parse(json_string);
	}catch(err){
		data = json_string;
	}

	if(typeof data != 'object') return data;

	let data_map: Map<string,any> = new Map();
	data_map.set(token, data);
	let data_list = [data];

	for(let i = 0; i < data_list.length; i++){
		let item = data_list[i];

		if(Array.isArray(item)){
			if(typeof item.Ab_lastElement_bA == 'string' && item.Ab_lastElement_bA.includes('<-[data]->')){
				(item.pop() as string).split(';').map(item=>{
					let [token, index] = item.split('<-[data]->');
					return [token, Number(index)] as [string, number];
				}).forEach(el=>data_map.set(el[0], item[el[1]]));
			}

			item.forEach((el, i, arr)=>{
				if(typeof el == 'string' && el.includes('<-[ref]->')){
					let token = el.replace('<-[ref]->', '');
					arr[i] = data_map.get(token);
				}else if(typeof el == 'object') data_list.push(el);
			});
		}else if(item != null){
			Reflect.ownKeys(item).forEach(key=>{
				let value = Reflect.get(item, key);

				if(typeof value == 'object') data_list.push(value);
				else if(typeof value == 'string' && value.includes('<-[ref]->')){
					let token = value.replace('<-[ref]->', '');
					value = data_map.get(token);
					Reflect.set(item, key, value);
				}

				if(typeof key == 'string' && key.includes('<-[data]->')){
					Reflect.deleteProperty(item, key);
					let [token, k] = key.split('<-[data]->');
					Reflect.set(item, k, value);
					data_map.set(token, value);
				}
			});
		}
	}

	return data;
}

abi.Ab_string_bA.Ab_random_bA = function(len, radix){
	[len, radix] = [[Number(len), 6], [Number(radix), 10]].map(_=>isNaN(_[0]) ? _[1] : _[0]);
	radix = Math.max(2, Math.min(36, radix));

	let str = Math.random().toString(radix).slice(2, 2+len);
	while(str.length < len) str += Math.random().toString(radix).slice(2, 2+len-str.length);
	return str;
}

abi.Ab_string_bA.Ab_randomToken_bA = function(len, radix, verify){
	[radix, verify] = abi.Ab_pickValueByType_bA([radix, verify], [['number', 10], ['function', undefined]]) as [number, (token: string)=>boolean];
	let token = abi.Ab_string_bA.Ab_random_bA(len, radix);

	if(typeof verify == 'function') while(!verify(token)) token = abi.Ab_string_bA.Ab_random_bA(len, radix);
	return token;
}

abi.Ab_string_bA.Ab_getAffix_bA = function(list){
	if(!list || list.length==0) return {prefix: '', suffix: '', max_length: 0};

	let prefix = list[0];
	let suffix = list[0];
	let pre_end = prefix.length;
	let suf_start = 0;
	let max_length = 0;

	list.forEach(el=>{
		if(el.length>max_length) max_length = el.length;

		if(!el.includes(prefix)){
			while(pre_end>0 && !el.includes(prefix.slice(0, pre_end))) pre_end--;
			prefix = prefix.slice(0, pre_end);
		}

		if(!el.includes(suffix)){
			while(suf_start<suffix.length-1 && !el.includes(suffix.slice(suf_start, suffix.length))) suf_start++;
			suffix = suffix.slice(suf_start, suffix.length);
		}
	});

	return {prefix, suffix, max_length};
}

abi.Ab_string_bA.Ab_normalLen_bA = function(value, len){
	value = typeof value == 'string' ? value : String(value);
	value = '_'+value;
	return value.padEnd(len+1, '_');
}

abi.Ab_string_bA.Ab_levenshtein_bA = function(a, b){
	const matrix: number[][] = [];
	let i: number;
	let j: number;
	for(i = 0; i <= a.length; i++) matrix[i] = [i];
	for(j = 0; j <= b.length; j++) matrix[0][j] = j;

	for(i = 1; i <= a.length; i++){
		for(j = 1; j <= b.length; j++){
			if(b.charAt(j-1) == a.charAt(i-1)) matrix[i][j] = matrix[i-1][j-1];
			else{
				matrix[i][j] = Math.min(
					matrix[i-1][j],
					matrix[i][j-1],
					matrix[i-1][j-1]
				) + 1;
			}
		}
	}

	return matrix[a.length][b.length];
}

abi.Ab_string_bA.Ab_findLikeStr_bA = function(target, list){
	let mostSimilar = list[0];
	let minDistance = abi.Ab_string_bA.Ab_levenshtein_bA(mostSimilar, target);

	for (const str of list) {
	  if (str === target) {
		return str;
	  }

	  const distance = abi.Ab_string_bA.Ab_levenshtein_bA(str, target);
	  if (distance < minDistance) {
		minDistance = distance;
		mostSimilar = str;
	  }
	}

	return mostSimilar;
}

abi.Ab_number_bA.Ab_PI_bA = Math.PI;
abi.Ab_number_bA.Ab_RAD_bA = Math.PI / 180;
abi.Ab_number_bA.Ab_DEG_bA = 180 / Math.PI;

abi.Ab_number_bA.Ab_inRange_bA = function(num, a, b){
	[num, a, b] = [num, a, b].map(_=>Number(_));
	if([num, a, b].includes(NaN)) return false;

	return num <= Math.max(a, b) && num >= Math.min(a, b);
}

abi.Ab_number_bA.Ab_getPrecision_bA = function(num){
	num = Number(num);
	if(isNaN(num) || Number.isInteger(num)) return 0;

	return (num % 1).toString().length - (num>=0 ? 2 : 3);
}

abi.Ab_number_bA.Ab_repair_bA = function(num){
	num = Number(num);
	if(isNaN(num)) return 0;
	if(Number.isInteger(num)) return num;

	let str = num.toString();
	if(str.length-str.indexOf('.') < 17) return num;

	let index = str.length-1;
	if(str[index]=='9') while(str[index]=='9') index--;
	else{
		index--;
		while(str[index]=='0') index--;
	}

	let weight = 10 ** (index-str.indexOf('.'));
	return Math.round(num * weight) / weight;
}

abi.Ab_number_bA.Ab_random_bA = function(a, b, precision){
	[a, b] = [a, b].map(_=>{
		_ = Number(_);
		return isNaN(_) ? 0 : _;
	});

	let range = b-a;
	if(precision==undefined){
		if(Math.abs(range)==1) precision = 2;
		else if(!Number.isInteger(range)){
			range = abi.Ab_number_bA.Ab_repair_bA(range);
			precision = abi.Ab_number_bA.Ab_getPrecision_bA(range);
			if(range == 10 ** -precision) precision += 2;
		}else precision = 0;
	}

	let weight = 10 ** precision;

	return abi.Ab_number_bA.Ab_repair_bA(a + Math.round(Math.random() * range * weight) / weight);
}

abi.Ab_number_bA.Ab_parse_bA = function(value, spare){
	[spare, value] = [spare, value].map(_=>typeof _=='number' ? _ : Number(_));
	return isNaN(value) ? isNaN(spare) ? 0 : spare : value;
}

abi.Ab_number_bA.Ab_fixedNum_bA = function(num, place, floor){
	let weight = 10 ** Math.round(abi.Ab_number_bA.Ab_parse_bA(place));
	num = num * weight;
	num = floor===true ? Math.floor(num) : floor===false ? Math.ceil(num) : Math.round(num);
	return abi.Ab_number_bA.Ab_repair_bA(num / weight);
};

abi.Ab_number_bA.Ab_limit_bA = function(num, a, b){
	[num, a, b] = [num, a, b].map(_=>abi.Ab_number_bA.Ab_parse_bA(_, 0));
	return Math.max(Math.min(a, b), Math.min(Math.max(a, b), num));
};

abi.Ab_array_bA.Ab_random_bA = function(list){
	if(!Array.isArray(list)) return list;
	if(list.length==0) return undefined;
	if(list.length==1) return list[0];
	return list[Math.floor(Math.random() * list.length)];
}

//@ts-ignore
abi.Ab_array_bA.Ab_randomeElement_bA = function(list, len, repetition){
	len = len ?? 1;
	if(repetition===false) len = Math.min(len, list.length);
	repetition = repetition ?? len>list.length;


	let index_list: number[] = [];
	while(index_list.length < len){
		let index = abi.Ab_number_bA.Ab_random_bA(0, list.length-1);
		if(!repetition) while(index_list.includes(index)) index = abi.Ab_number_bA.Ab_random_bA(0, list.length-1);
		index_list.push(index);
	}

	return index_list.map(index=>list[index]);
}

abi.Ab_array_bA.Ab_last_bA = function(list, index){
	if(!Array.isArray(list)) return list;
	if(list.length==0) return undefined;

	if(index){
		index = Number(index);
		index = Math.floor(isNaN(index) ? 0 : index) + 1;
		index = Math.max(1, Math.min(list.length, index));
	}else index = 1;

	return list[list.length-index];
}

//@ts-ignore
abi.Ab_array_bA.Ab_clear_bA = function(list){
	return (Array.isArray(list) && list.length>0) ? list.splice(0, list.length) : [];
}

abi.Ab_array_bA.Ab_remove_bA = function(arr: any[], element: any, head?: boolean){
	let verify: (el: any, i: number, arr: any[])=>boolean;
	if(element instanceof Function) verify = element;
	else verify = (el: any, i: number, arr: any[])=>el==element;

	let [point, end, direction] = (head??true) ? [0, arr.length, 1] : [arr.length-1, -1, -1];

	while(point!=end){
		if(verify(arr[point], point, arr)) break;
		point += direction;
	}

	return point.Ab_inRange_bA(0, arr.length-1) ? arr.splice(point, 1)[0] : undefined;
}

abi.Ab_array_bA.Ab_create_bA = function(size, call){
	return new Array(size).fill(null, 0, size).map((_, i)=>call(i));
}

abi.Ab_color_bA.Ab_common_bA = { aliceblue: '#f0f8ff', antiquewhite: '#faebd7', aqua: '#00ffff', aquamarine: '#7fffd4', azure: '#f0ffff', beige: '#f5f5dc', bisque: '#ffe4c4', black: '#000000', blanchedalmond: '#ffebcd', blue: '#0000ff', blueviolet: '#8a2be2', brown: '#a52a2a', burlywood: '#deb887', cadetblue: '#5f9ea0', chartreuse: '#7fff00', chocolate: '#d2691e', coral: '#ff7f50', cornflowerblue: '#6495ed', cornsilk: '#fff8dc', crimson: '#dc143c', cyan: '#00ffff', darkblue: '#00008b', darkcyan: '#008b8b', darkgoldenrod: '#b8860b', darkgray: '#a9a9a9', darkgreen: '#006400', darkkhaki: '#bdb76b', darkmagenta: '#8b008b', darkolivegreen: '#556b2f', darkorange: '#ff8c00', darkorchid: '#9932cc', darkred: '#8b0000', darksalmon: '#e9967a', darkseagreen: '#8fbc8f', darkslateblue: '#483d8b', darkslategray: '#2f4f4f', darkturquoise: '#00ced1', darkviolet: '#9400d3', deeppink: '#ff1493', deepskyblue: '#00bfff', dimgray: '#696969', dodgerblue: '#1e90ff', firebrick: '#b22222', floralwhite: '#fffaf0', forestgreen: '#228b22', fuchsia: '#ff00ff', gainsboro: '#dcdcdc', ghostwhite: '#f8f8ff', gold: '#ffd700', goldenrod: '#daa520', gray: '#808080', green: '#008000', greenyellow: '#adff2f', honeydew: '#f0fff0', hotpink: '#ff69b4', indianred: '#cd5c5c', indigo: '#4b0082', ivory: '#fffff0', khaki: '#f0e68c', lavender: '#e6e6fa', lavenderblush: '#fff0f5', lawngreen: '#7cfc00', lemonchiffon: '#fffacd', lightblue: '#add8e6', lightcoral: '#f08080', lightcyan: '#e0ffff', lightgoldenrodyellow: '#fafad2', lightgray: '#d3d3d3', lightgreen: '#90ee90', lightpink: '#ffb6c1', lightsalmon: '#ffa07a', lightseagreen: '#20b2aa', lightskyblue: '#87cefa', lightslategray: '#778899', lightsteelblue: '#b0c4de', lightyellow: '#ffffe0', lime: '#00ff00', limegreen: '#32cd32', linen: '#faf0e6', magenta: '#ff00ff', maroon: '#800000', mediumaquamarine: '#66cdaa', mediumblue: '#0000cd', mediumorchid: '#ba55d3', mediumpurple: '#9370db', mediumseagreen: '#3cb371', mediumslateblue: '#7b68ee', mediumspringgreen: '#00fa9a', mediumturquoise: '#48d1cc', mediumvioletred: '#c71585', midnightblue: '#191970', mintcream: '#f5fffa', mistyrose: '#ffe4e1', moccasin: '#ffe4b5', navajowhite: '#ffdead', navy: '#000080', oldlace: '#fdf5e6', olive: '#808000', olivedrab: '#6b8e23', orange: '#ffa500', orangered: '#ff4500', orchid: '#da70d6', palegoldenrod: '#eee8aa', palegreen: '#98fb98', paleturquoise: '#afeeee', palevioletred: '#db7093', papayawhip: '#ffefd5', peachpuff: '#ffdab9', peru: '#cd853f', pink: '#ffc0cb', plum: '#dda0dd', powderblue: '#b0e0e6', purple: '#800080', rebeccapurple: '#663399', red: '#ff0000', rosybrown: '#bc8f8f', royalblue: '#4169e1', saddlebrown: '#8b4513', salmon: '#fa8072', sandybrown: '#f4a460', seagreen: '#2e8b57', seashell: '#fff5ee', sienna: '#a0522d', silver: '#c0c0c0', skyblue: '#87ceeb', slateblue: '#6a5acd', slategray: '#708090', snow: '#fffafa', springgreen: '#00ff7f', steelblue: '#4682b4', tan: '#d2b48c', teal: '#008080', thistle: '#d8bfd8', tomato: '#ff6347', turquoise: '#40e0d0', violet: '#ee82ee', wheat: '#f5deb3', white: '#ffffff', whitesmoke: '#f5f5f5', yellow: '#ffff00', yellowgreen: '#9acd32', };

abi.Ab_color_bA.Ab_toHexColor_bA = function(color){
	let hex: string;
	if(color.startsWith('#')) hex = color.slice(1, 7);
	else if(!isNaN(Number('0x'+color))) hex = color.slice(0, 6);

	if(hex && [3, 4, 6, 8].includes(hex.length)) return abi.Ab_color_bA.Ab_normalizeHexColor_bA('#' + hex);

	color = color.toLowerCase();
	color = Reflect.has(abi.Ab_color_bA.Ab_common_bA, color) ? color : 'white';
	return Reflect.get(abi.Ab_color_bA.Ab_common_bA, color);
}

abi.Ab_color_bA.Ab_normalizeHexColor_bA = function(color){
	let hex: string;
	if(color.startsWith('#') && !isNaN(Number('0x'+color.slice(1, color.length)))) hex = color.replace('#', '');
	else hex = abi.Ab_color_bA.Ab_toHexColor_bA(color).replace('#', '');

	if([3, 4].includes(hex.length)) hex = hex.split('').map(bit=>bit+bit).join('');

	if(hex.length>6) hex = hex.substring(0, 6);
	else hex.padEnd(6, '0');

	return '#' + hex.toUpperCase();
}

abi.Ab_color_bA.Ab_toRGBColor_bA = function(color){
	let hex = abi.Ab_color_bA.Ab_normalizeHexColor_bA(color);
	let [r, g, b] = hex.slice(1, hex.length).match(/.{1,2}/g).map(c=>parseInt(c, 16));
	return [r, g, b];
}

abi.Ab_color_bA.Ab_toHSLColor_bA = function(color){
	let [r, g, b] = abi.Ab_color_bA.Ab_toRGBColor_bA(color);

	let max = Math.max(r, g, b);
	let min = Math.min(r, g, b);
	let h, s, l = (max + min) / 2;

	if (max === min) {
	  // 如果最大值和最小值相等，表示颜色是灰度的，色相设为0
	  h = s = 0;
	} else {
	  let d = max - min;
	  s = l > 0.5 ? d / (2 - max - min) : d / (max + min);

	  switch (max) {
		case r: h = (g - b) / d + (g < b ? 6 : 0); break;
		case g: h = (b - r) / d + 2; break;
		case b: h = (r - g) / d + 4; break;
	  }

	  h /= 6;
	}

	return [h * 360, s * 100, l * 100].map(v=>Math.round(v)) as [number, number, number];
}

abi.Ab_color_bA.Ab_RGB2Hex_bA = function(r, g, b){
	return '#' + [r, g, b].map(v=>v.toString(16).padStart(2, '0')).join('');
}

abi.Ab_color_bA.Ab_HSL2Hex_bA = function(h, s, l){
	h /= 360; // 将色相转换为 [0, 1] 范围
	s /= 100; // 将饱和度转换为 [0, 1] 范围
	l /= 100; // 将亮度转换为 [0, 1] 范围

	let r, g, b;

	if (s === 0) {
	  r = g = b = l; // 如果饱和度为 0，则为灰度色
	} else {
	  let hue2rgb = function hue2rgb(p, q, t) {
		if (t < 0) t += 1;
		if (t > 1) t -= 1;
		if (t < 1 / 6) return p + (q - p) * 6 * t;
		if (t < 1 / 2) return q;
		if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
		return p;
	  };

	  let q = l < 0.5 ? l * (1 + s) : l + s - l * s;
	  let p = 2 * l - q;

	  r = hue2rgb(p, q, h + 1 / 3);
	  g = hue2rgb(p, q, h);
	  b = hue2rgb(p, q, h - 1 / 3);
	}

	// 将 [0, 1] 范围的 RGB 值转换为 [0, 255] 范围并四舍五入
	r = Math.round(r * 255);
	g = Math.round(g * 255);
	b = Math.round(b * 255);

	return abi.Ab_color_bA.Ab_RGB2Hex_bA(r, g, b);
}

abi.Ab_color_bA.Ab_logColorLike_bA = function(cut){
	let list = Reflect.ownKeys(abi.Ab_color_bA.Ab_common_bA).filter((color: string)=>color.includes(cut??'')) as string[];

	if(list.length<=0) return void 0;

	let format_str = '';
	let style_list = [];
	let default_style = 'padding: 0 0.5em; font-weight: bolder;';
	let start_style = 'border-top-left-radius: 0.5em 0.5em; border-bottom-left-radius: 0.5em 0.5em;';
	let end_style = 'border-top-right-radius: 0.5em 0.5em; border-bottom-right-radius: 0.5em 0.5em;';

	list.forEach(color=>{
		let hex = abi.Ab_color_bA.Ab_normalizeHexColor_bA(Reflect.get(abi.Ab_color_bA.Ab_common_bA, color));
		let [h, s, l] = abi.Ab_color_bA.Ab_toHSLColor_bA(hex);

		format_str += '%c' + color + '%c' + hex;

		let font_color = abi.Ab_color_bA.Ab_HSL2Hex_bA(h, s>0 ? 100 : 0, l>50 ? 20 : 80);

		let style_desc = [
			['background-color', hex],
			['color', font_color]
		].map(item=>item.join(':')).join(';') + ';';

		let style_real = [
			['background-color', hex],
			['color', font_color]
		].map(item=>item.join(':')).join(';') + ';';

		style_list.push(...[start_style + style_desc, style_real + end_style].map(s=>s+default_style));
	});

	console.log(format_str, ...style_list);
}

abi.Ab_color_bA.Ab_logFullColorLine_bA = function(size){
	size = size??3;
	let format_str = '';
	let style_list = [];
	let default_style = 'font-weight: lighter; line-height: 5px; font-size: ' + size + 'px;';

	for(let i = 0; i < 360; i++){
		format_str += '%c ';
		style_list.push(default_style + 'background-color: ' + abi.Ab_color_bA.Ab_HSL2Hex_bA(i, 100, 50));
	}

	console.log(format_str, ...style_list);
}

var Ab_LOG_DEBUG_MODE_bA = true;
abi.Ab_log_bA.Ab_enable_debug_mode_bA = function(enable){
	Ab_LOG_DEBUG_MODE_bA = enable;
}

abi.Ab_log_bA.Ab_tag_bA = function(tag, ...args){
	//return void 0;
	let str = '', colors = [];

	if(typeof(tag)=='string' && tag.includes(':')){
		let joint_call: (item: string[], idx: number)=>void;
		if(Ab_LOG_DEBUG_MODE_bA){
			joint_call = function(item, idx){
				str += '%c' + item[0];
				let [h, s, l] = abi.Ab_color_bA.Ab_toHSLColor_bA(item[1]);
				let clr = [
					['background-color', abi.Ab_color_bA.Ab_normalizeHexColor_bA(item[1])],
					['color', abi.Ab_color_bA.Ab_HSL2Hex_bA(h, s>0 ? 100 : 0, l>50 ? 20 : 80)]
				].reduce((t, el)=>{
					t.push(el.join(':'));
					return t;
				}, [
					'padding:0 0.5em',
					'font-weight: bolder',
				]).join('; ') + '; ';
				colors.push(clr);
			};
		}else{
			joint_call = function(item, idx){
				str += (idx==0?'':' ') + item[0];
			}
		}

		tag.split(';').map(s=>{
			s = s.trim();
			if(s.length==0) return [];
			else return s.split(':').map(kv=>kv.trim());
		}).filter(item=>item.length==2 && item.every(kv=>kv.length>0))
		.forEach((item, idx)=>joint_call(item, idx));

		joint_call = undefined;
	}

	if(Ab_LOG_DEBUG_MODE_bA){
		colors[0] += 'border-top-left-radius: 0.5em 0.5em; border-bottom-left-radius: 0.5em 0.5em; ';
		colors[colors.length-1] += 'border-top-right-radius: 0.5em 0.5em; border-bottom-right-radius: 0.5em 0.5em; ';
		console.groupCollapsed(str + '%c', ...colors, '', ...args);
		console.trace();
		console.groupEnd();
	}else{
		if(args.length>0){
			args.forEach(param=>{
				str += ' ';
				switch(typeof(param)){
					case 'object': str += JSON.stringify(param); break;
					default: str += String(param);
				}
			});
		}

		console.log('[LOG@TAG]:', str);
	}
}

abi.Ab_log_bA.Ab_warn_bA = function(...args){
	abi.Ab_log_bA.Ab_tag_bA("WARN:orange;>>:tomato", ...args);
}

abi.Ab_local_bA.Ab_read_bA = function(key, out){
	let data_json = localStorage.getItem(key.Ab_encodeBtoA_bA);

	if(data_json){
		data_json = data_json.Ab_decodeAtoB_bA;
		if(out) abi.Ab_fusionData_bA(out, JSON.parse(data_json));
		else out = JSON.parse(data_json);
	}

	return out as any;
}

abi.Ab_local_bA.Ab_save_bA = function(key, data){
	localStorage.setItem(key.Ab_encodeBtoA_bA, JSON.stringify(data).Ab_encodeBtoA_bA);
}

abi.Ab_time_bA.Ab_ONE_DAY_bA = 24 * 60 * 60 * 1000;

abi.Ab_time_bA.Ab_sameDay_bA = function(d1, d2){
	if ([d1, d2].includes(undefined)) return false;
	let [day1, day2] = [d1, d2].map(d => new Date(d));
	return day1.toDateString() === day2.toDateString();
};
